package com.zzh.camera2.utils

import android.graphics.*
import android.media.Image
import android.os.Environment
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import kotlin.math.ceil


/**
 * @author ：zuhui.zhang
 * time ： 2021/4/23 15:03
 */
object ImageUtils {
    // add by zzh, optimize time consume
    fun getBytesFromImageToNV21(image: Image?): ByteArray? {
        try {
            if (image == null || image.planes.isEmpty()) {
                return null
            }
            val planes = image.planes.clone()

            val pictureSize = image.width * image.height
            val nv21 = ByteArray(pictureSize * 3 / 2)
            var index: Int
            // nv21 data like YYYYVUVU, image format is YV12:YYYYVVUU, y placed on planes[0], u on planes[1], v on planes[2]
            // image format decide by DisplayFragment.mFormat
            if (planes.size > 2) {
                // y
                val yBuffer = planes[0].buffer
                yBuffer[nv21, 0, pictureSize]
//                System.arraycopy(planes[0].buffer, 0, nv21, 0, pictureSize)
                // v pixel stride is equal u pixel stride
                val uvPixelStride = planes[2].pixelStride
                index = pictureSize
                // u
                val uBuffer = planes[1].buffer

                // v
                val vBuffer = planes[2].buffer
                // v bytes length is equal u bytes length
                var uv = 0
                while (uv < vBuffer.capacity()) {
                    nv21[index++] = vBuffer[uv]
                    nv21[index++] = uBuffer[uv]
                    uv += uvPixelStride
                }
            }
            return nv21
        } catch (e: Exception) {
            LogUtils.d("ImageUtils", "getBytesFromImageToNV21 1 param function error=" + e.message)
        } finally {
            image?.close()
        }
        return null
    }

    // add by zzh, optimize time consume
    fun getBytesFromImageToNV21(
        plane0Array: ByteArray?, plane1Array: ByteArray?, plane2Array: ByteArray?,
        pixelStride: Int?, imageWidth: Int, imageHeight: Int
    ): ByteArray? {
        try {
            if (plane0Array == null || plane1Array == null || plane2Array == null || pixelStride == null) {
                return null
            }
            val pictureSize = imageWidth * imageHeight
            val nv21 = ByteArray(pictureSize * 3 / 2)
            var index: Int
            // nv21 data like YYYYVUVU, image format is YV12:YYYYVVUU, y placed on planes[0], u on planes[1], v on planes[2]
            // image format decide by DisplayFragment.mFormat
            // y data
            var yIndex = 0
            while (yIndex < pictureSize) {
                nv21[yIndex] = plane0Array[yIndex]
                yIndex++
            }
//            yBuffer[nv21, 0, pictureSize]
//                System.arraycopy(planes[0].buffer, 0, nv21, 0, pictureSize)
            // v pixel stride is equal u pixel stride
            val uvPixelStride = pixelStride!!
            index = pictureSize
            // u
            val uBuffer = plane1Array

            // v
            val vBuffer = plane2Array
            // v bytes length is equal u bytes length
            var uv = 0
            while (uv < vBuffer.size) {
                nv21[index++] = vBuffer[uv]
                nv21[index++] = uBuffer[uv]
                uv += uvPixelStride
            }
            return nv21
        } catch (e: Exception) {
            LogUtils.d("ImageUtils", "getBytesFromImageToNV21 6 param function error=" + e.message)
        }
        return null
    }

    // YV12: YYYYYYYY VV UU
    fun yv12ToNV21(byteArray: ByteArray, width: Int, height: Int): ByteArray {
        val yuvBytes = ByteArray(width * height * 3 / 2)
        try {
            // copy y
            val size = width * height
            System.arraycopy(byteArray, 0, yuvBytes, 0, size)
            var uvIndex = size
            var uByteIndex = size * 5 / 4

            for (vByteIndex in size until size * 5 / 4) {
                // copy v
                yuvBytes[uvIndex++] = byteArray[vByteIndex]
                // copy u
                yuvBytes[uvIndex++] = byteArray[uByteIndex++]
            }
            return yuvBytes
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        return yuvBytes
    }

    @Synchronized
    fun adjustPhotoRotation(bm: Bitmap, orientationDegree: Int): Bitmap? {
        val m = Matrix()
        m.setRotate(orientationDegree.toFloat(), bm.width.toFloat() / 2, bm.height.toFloat() / 2)
        val targetX: Float
        val targetY: Float
        if (orientationDegree == 90) {
            targetX = bm.height.toFloat()
            targetY = 0f
        } else {
            targetX = bm.height.toFloat()
            targetY = bm.width.toFloat()
        }
        val values = FloatArray(9)
        m.getValues(values)
        val x1 = values[Matrix.MTRANS_X]
        val y1 = values[Matrix.MTRANS_Y]
        m.postTranslate(targetX - x1, targetY - y1)
        val bm1 = Bitmap.createBitmap(bm.height, bm.width, Bitmap.Config.ARGB_8888)
        val paint = Paint()
        val canvas = Canvas(bm1)
        canvas.drawBitmap(bm, m, paint)
        return bm1
    }

    fun adjustPhotoRotation2(bm: Bitmap, orientationDegree: Int): Bitmap? {
        val m = Matrix()
        m.setRotate(orientationDegree.toFloat(), bm.width.toFloat() / 2, bm.height.toFloat() / 2)
        try {
            return Bitmap.createBitmap(bm, 0, 0, bm.width, bm.height, m, true)
        } catch (ex: OutOfMemoryError) {
        }
        return null
    }

    /**
     * 保存图片
     */
    fun saveBitmap(file: File?, bitmap: Bitmap): Boolean {
        var out: FileOutputStream? = null
        try {
            out = FileOutputStream(file)
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)
            return true
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        } finally {
            try {
                out?.close()
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }
        return false
    }

    //NV21: YYYY VUVU
    fun NV21_mirror(nv21_data: ByteArray, width: Int, height: Int): ByteArray? {
        var i: Int
        var left: Int
        var right: Int
        var temp: Byte
        var startPos = 0
        // mirror Y
        i = 0
        while (i < height) {
            left = startPos
            right = startPos + width - 1
            while (left < right) {
                temp = nv21_data[left]
                nv21_data[left] = nv21_data[right]
                nv21_data[right] = temp
                left++
                right--
            }
            startPos += width
            i++
        }
        // mirror U and V
        val offset = width * height
        startPos = 0
        i = 0
        while (i < height / 2) {
            left = offset + startPos
            right = offset + startPos + width - 2
            while (left < right) {
                temp = nv21_data[left]
                nv21_data[left] = nv21_data[right]
                nv21_data[right] = temp
                left++
                right--
                temp = nv21_data[left]
                nv21_data[left] = nv21_data[right]
                nv21_data[right] = temp
                left++
                right--
            }
            startPos += width
            i++
        }
        return nv21_data
    }

    var index = 0
    fun saveNV21(data: ByteArray, width: Int, height: Int) {
        if (index >= 5) {
            return
        }
        //保存一张照片
        val fileName = "IMG_" + index++.toString() + ".jpg" //jpeg文件名定义
        val sdRoot = Environment.getExternalStorageDirectory() //系统路径
        val dir = "/jpeg/" //文件夹名
        val mkDir = File(sdRoot, dir)
        if (!mkDir.exists()) {
            //目录不存在，则创建
            mkDir.mkdirs()
        }
        val pictureFile = File(sdRoot, dir + fileName)
        if (!pictureFile.exists()) {
            try {
                pictureFile.createNewFile()
                val filecon = FileOutputStream(pictureFile)
                // 将NV21 data保存成YuvImage
                val image = YuvImage(data, ImageFormat.NV21, width, height, null)
                // 图像压缩 // 将NV21格式图片，以质量70压缩成Jpeg，并得到JPEG数据流
                image.compressToJpeg(Rect(0, 0, image.width, image.height), 70, filecon)
                filecon.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    fun getYV12ImagePixelSize(previewWidth: Int, previewHeight: Int): Int {
        val yStride = (ceil(previewWidth / 16.0) * 16).toInt()
        val uvStride = (ceil(yStride / 2 / 16.0) * 16).toInt()
        val ySize = yStride * previewHeight
        val uvSize = uvStride * previewHeight / 2
        return ySize + uvSize * 2
    }
}